package com.devops.shiro.graphql;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.devops.admin.model.TokenResponse;
import com.devops.common.annotation.DOSLog;
import com.devops.common.utils.Asserts;
import com.devops.shiro.config.KeycloakAuthenticationToken;
import com.devops.shiro.util.EntityUtils;
import com.devops.shiro.util.ShiroUtils;
import com.devops.shiro.vm.BaseUserLoginVm;
import com.devops.shiro.vm.BaseUserVM;
import com.devops.shiro.vm.LoginOutVm;
import com.devops.shiro.vm.LoginUserVM;
import com.netflix.graphql.dgs.DgsComponent;
import com.netflix.graphql.dgs.DgsQuery;
import com.netflix.graphql.dgs.InputArgument;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Value;

import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName LoginDataFetcher
 * @Description TODO
 * @Author heyabo
 * @Date 2022/8/3 16:28
 * @Version 1.0
 **/
@Slf4j
@DgsComponent
public class LoginDataFetcher {

    @Value("${keycloak.auth-server-url}")
    private String keycloakServerUrl;
    @Value("${keycloak.realm}")
    private String keycloakRealm;
    @Value("${keycloak.resource}")
    private String clientId;
    @Value("${keycloak.credentials.secret}")
    private String clientSecret;
    @Value("${keycloak.redirect_url}")
    private String redirectUrl;

    /**
     * @Author heyabo
     * @Description 返回登录界面
     * @Date 11:52 2022/4/19
     * @Param []
     * @return java.lang.String
     **/
    @DgsQuery
    public String loginPage() {
        return "login";
    }

    /**
     * @Author heyabo
     * @Description 用户登录
     * @Date 11:51 2022/4/19
     * @Param [loginUser]
     * @return com.devops.common.utils.Message<java.lang.Object>
     **/
    @DgsQuery
    @DOSLog(description = "登录")
    public BaseUserLoginVm login(@InputArgument LoginUserVM loginUser) throws IncorrectCredentialsException {
        Asserts.notEmpty(loginUser,"登录用户不能为空");
        String account=loginUser.getLoginName();
        String password=loginUser.getPassword();
        UsernamePasswordToken token = new UsernamePasswordToken(account,password,false);
        token.setRememberMe(true);

        Subject currentUser = SecurityUtils.getSubject();
        BaseUserLoginVm loginVm = new BaseUserLoginVm();
        try {
            currentUser.login(token);
        } catch(IncorrectCredentialsException e){
            loginVm.setCode(500);
            loginVm.setError("账号或密码错误");
            return loginVm;
        } catch (AuthenticationException e) {
            loginVm.setCode(500);
            loginVm.setError("认证错误");
            return loginVm;
        }
        Object primaryPrincipal = currentUser.getPrincipals().getPrimaryPrincipal();
        BaseUserVM user = EntityUtils.entity2VM(primaryPrincipal,BaseUserVM.class);
        SecurityUtils.getSubject().getSession().setTimeout(24 * 60 * 60 * 1000L);
        loginVm.setCode(200);
        loginVm.setData(user);
        return loginVm;
    }

    /**
     * @Author heyabo
     * @Description 退出登录
     * @Date 11:52 2022/4/19
     * @Param []
     * @return com.devops.common.utils.Message<java.lang.String>
     **/
    @DgsQuery
    @DOSLog(description = "退出登录")
    public LoginOutVm loginOut() {
        LoginOutVm loginOutVm = new LoginOutVm();
        loginOutVm.setMessage(ShiroUtils.currentUser().getIdToken());
        loginOutVm.setCode(200);
        ShiroUtils.getSubjct().logout();
        return loginOutVm;
    }

    @DgsQuery
    @DOSLog(description = "使用code登录")
    public BaseUserLoginVm loginByCode(@InputArgument String code) {
        Map<String, Object> params = new HashMap<>();
        params.put("client_id", clientId);
        params.put("client_secret", clientSecret);
        params.put("grant_type", "authorization_code");
        params.put("code", code);
        params.put("redirect_uri", redirectUrl);
        HttpResponse response = HttpUtil.createPost(StrUtil.format("{}/realms/{}/protocol/openid-connect/token",
                        keycloakServerUrl, keycloakRealm))
                .form(params)
                .timeout(6000)
                .execute();
        if (200 != response.getStatus()) {
            return new BaseUserLoginVm(null, 500, "invalid code");
        }
        log.info("login response {}", response.body());
        TokenResponse tokenResponse = JSONUtil.toBean(response.body(), TokenResponse.class);

        KeycloakAuthenticationToken token = new KeycloakAuthenticationToken(tokenResponse.getIdToken());
        Subject currentUser = SecurityUtils.getSubject();
        try {
            currentUser.login(token);
        } catch (AuthenticationException e) {
            return new BaseUserLoginVm(null, 500, e.getMessage());
        }
        Object primaryPrincipal = currentUser.getPrincipals().getPrimaryPrincipal();
        BaseUserVM user = EntityUtils.entity2VM(primaryPrincipal,BaseUserVM.class);
        user.setPassword(null);
        user.setIdToken(null);
        SecurityUtils.getSubject().getSession().setTimeout(tokenResponse.getExpiresIn() * 1000L);
        return new BaseUserLoginVm(user, 200, null);
    }
}
